﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Practices.Composite.Events;

namespace Might.Infrastructure.DOM {
    /// <summary>
    /// Structure used for storing single update.
    /// </summary>
    public struct DomNodeChange {
        /// <summary>
        /// Origin of this update
        /// </summary>
        public object Sender;

        /// <summary>
        /// Changed property
        /// </summary>
        public string PropertyName;
    }


    /// <summary>
    /// Implementation of INotifychanged interface used mainly in DomNode
    /// </summary>
    public class NotifyChangeBase : INotifyChange {
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region INotifyChange Members

        public event EventHandler Changed;

        public virtual void StartUpdate() {
            changes.Clear();
            notify = false;
        }

        public virtual void FinishUpdate() {
            FinishUpdate(null);
        }

        public void FinishUpdate(object exceptObj) {
            if (PropertyChanged != null) {
                Delegate[] delegates = PropertyChanged.GetInvocationList();
                foreach (Delegate del in delegates) {
                    if (del.Target != exceptObj) {
                        foreach (DomNodeChange change in changes) {
                            del.DynamicInvoke(change.Sender, new PropertyChangedEventArgs(change.PropertyName));
                        }
                    }
                }
            }

            if (Changed != null) {
                Delegate[] delegates = Changed.GetInvocationList();
                foreach (Delegate del in delegates) {
                    if (del.Target != exceptObj) {
                        del.DynamicInvoke(this, null);
                    }
                }
            }

            notify = true;
            changes.Clear();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="propName"></param>
        protected void OnChanged(object sender, string propName) {
            if (notify) {
                if (Changed != null) {
                    Changed(sender, null);
                }
                if (PropertyChanged != null) {
                    var e = new PropertyChangedEventArgs(propName);
                    PropertyChanged(sender, e);
                }
            } else {
                changes.Add(new DomNodeChange {Sender = sender, PropertyName = propName});
            }
        }

        #endregion

        #region Internals

        /// <summary>
        /// 
        /// </summary>
        private readonly List<DomNodeChange> changes = new List<DomNodeChange>();

        /// <summary>
        /// 
        /// </summary>
        private bool notify = true;

        #endregion
    }
}
